package com.ns.school.common.shiro.realm;

import com.ns.school.common.exception.UserTypeAccountException;
import com.ns.school.common.utils.Constants;
import com.ns.school.common.utils.Encodes;
import com.ns.school.po.sys.Menu;
import com.ns.school.po.sys.Role;
import com.ns.school.po.sys.User;
import com.ns.school.enums.LoginTypeEnum;
import com.ns.school.service.sys.UserService;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.lang.util.ByteSource;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.web.subject.WebSubject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

@Slf4j
@Component(value = "authRealm")
public class AuthRealm extends AuthorizingRealm {

    @Lazy
    @Autowired
    private UserService userService;

    /**
     * 权限配置类
     *
     * @MethodName doGetAuthorizationInfo
     * @Param [principalCollection]
     * @Return AuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 1.从principals中获取登录用户的信息
        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
        log.info("doGetAuthorizationInfo:{}", shiroUser.getLoginName());
        // 2.利用登录用户的信息获取当前用户的角色（有数据库的话，从数据库中查询）
        User userDB = userService.findUserByLoginName(shiroUser.getLoginName());
        // 3.创建SimpleAuthorizationInfo，并设置其roles属性
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 放置用户角色的set集合(不重复)
        Set<Role> roles = userDB.getRoleLists();
        Set<String> roleNames = new HashSet<>();
        for (Role role : roles) {
            if (StringUtils.isNotBlank(role.getName())) {
                roleNames.add(role.getName());
            }
        }
        Set<Menu> menus = userDB.getMenus();
        Set<String> permissions = new HashSet<>();
        for (Menu menu : menus) {
            if (StringUtils.isNotBlank(menu.getPermission())) {
                permissions.add(menu.getPermission());
            }
        }
        info.setRoles(roleNames);
        info.setStringPermissions(permissions);
        return info;
    }

    /**
     * 认证配置类
     *
     * @MethodName doGetAuthenticationInfo
     * @Param [authenticationToken]
     * @Return AuthenticationInfo
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = (String) token.getPrincipal();
        User userDB = userService.findUserByLoginName(username);
        log.info("doGetAuthenticationInfo:{}", username);
        if (userDB == null) {
            throw new UnknownAccountException();// 没找到帐号
        }
        if (Boolean.TRUE.equals(userDB.getLocked())) {
            throw new LockedAccountException(); // 帐号锁定
        }
        ServletRequest request = ((WebSubject) SecurityUtils.getSubject()).getServletRequest();
        HttpSession httpSession = ((HttpServletRequest) request).getSession();
        Object attribute = httpSession.getAttribute("loginType");
        LoginTypeEnum loginType = attribute == null ? null : (LoginTypeEnum) attribute;
        if (LoginTypeEnum.ADMIN.equals(loginType)) {
            if (Boolean.FALSE.equals(userDB.getAdminUser())) {
                throw new UserTypeAccountException(); // 帐号不是后台账户
            }
        }
        byte[] salt = Encodes.decodeHex(userDB.getSalt());
        return new SimpleAuthenticationInfo(
                new ShiroUser(userDB.getId(), userDB.getLoginName(), userDB.getNickName(), userDB.getIcon()),
                userDB.getPassword(), // 密码
                ByteSource.Util.bytes(salt),
                getName()  // realm name
        );
    }

    public void removeUserAuthorizationInfoCache(String username) {
        SimplePrincipalCollection pc = new SimplePrincipalCollection();
        pc.add(username, super.getName());
        super.clearCachedAuthorizationInfo(pc);
    }

    /**
     * 设定登录密码校验算法为Hash模式
     * 当前类被spring容器初始化的时候，springboot启动的时候，这个方法就会被调用一次
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(Constants.HASH_ALGORITHM);
        matcher.setHashIterations(Constants.HASH_INTERATIONS);
        setCredentialsMatcher(matcher);
        log.info("设定登录密码校验算法为Hash模式-完成");
    }

}
